package com.hinacom.pix.client;

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.model.AbstractMessage;
import ca.uhn.hl7v2.model.DataTypeException;
import ca.uhn.hl7v2.model.v231.message.*;
import ca.uhn.hl7v2.model.v231.segment.*;
import ca.uhn.hl7v2.parser.Parser;
import com.hinacom.pix.entity.*;
import com.hinacom.pix.serviceproxy.PixClient;
import com.hinacom.pix.serviceproxy.ReceiveListener;
import org.apache.log4j.Logger;

import java.io.ByteArrayInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.PropertyResourceBundle;
import java.util.ResourceBundle;
import java.util.UUID;


/**
 * Created by fyu on 2016/8/24.
 */
public class PIXSource extends BaseClient {

    private static Logger logger = Logger.getLogger(PIXSource.class);

    private static String sendingApplicationName;
    private static String sendingFacilityName;
    private static String receivingApplicationName;
    private static String receivingFacilityName;
    private static String host;
    private static int port;

    static {
        ResourceBundle bundle = PropertyResourceBundle.getBundle("pix");
        host = bundle.getString("v2-pix-host");
        String pixPort = bundle.getString("v2-pix-port");
        port = Integer.parseInt(pixPort);
        sendingApplicationName = bundle.getString("sending-application-name");
        sendingFacilityName = bundle.getString("sending-facility-name");
        receivingApplicationName = bundle.getString("receiving-application-name");
        receivingFacilityName = bundle.getString("receiving-facility-name");
    }


    private void fillMSH(MSH msh, String messageType) throws HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        msh.getSendingApplication().getNamespaceID().setValue(sendingApplicationName);
        msh.getSendingFacility().getHd1_NamespaceID().setValue(sendingFacilityName);
        msh.getReceivingApplication().getNamespaceID().setValue(receivingApplicationName);
        msh.getReceivingFacility().getHd1_NamespaceID().setValue(receivingFacilityName);
        msh.getMessageControlID().setValue(UUID.randomUUID().toString());
        msh.getDateTimeOfMessage().parse(now);
        msh.getMessageType().getMessageStructure().setValue(messageType);
    }

    private void fillPID(PID pid, Patient patient) throws HL7Exception {
        PatientIdentifier internalIdentifier = patient.getInternalIdentifier();
        AssigningAuthority assigningAuthority = internalIdentifier.getAssigningAuthority();

        // internal patient id
        pid.getPatientIdentifierList(0).getID().setValue(internalIdentifier.getId());
        pid.getPatientIdentifierList(0).getCheckDigit().setValue(internalIdentifier.getCheckDigit());
        pid.getPatientIdentifierList(0).getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(internalIdentifier.getSchemeEmployed());

        pid.getPatientIdentifierList(0).getAssigningAuthority().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
        pid.getPatientIdentifierList(0).getAssigningAuthority().getUniversalID().setValue(assigningAuthority.getUniversalId());
        pid.getPatientIdentifierList(0).getAssigningAuthority().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());

        pid.getPatientIdentifierList(0).getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

        assigningAuthority = internalIdentifier.getAssigningFacility();
        if (assigningAuthority != null) {
            pid.getPatientIdentifierList(0).getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
            pid.getPatientIdentifierList(0).getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
            pid.getPatientIdentifierList(0).getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
        }

        // patient name
        PatientName patientName = patient.getPatientName();
        pid.getPatientName(0).getFamilyLastName().parse(patientName.getLastName());
        pid.getPatientName(0).getGivenName().setValue(patientName.getFirstName());
        pid.getPatientName(0).getMiddleInitialOrName().setValue(patientName.getMiddleName());
        pid.getPatientName(0).getNameTypeCode().setValue("L");
        pid.getPatientName(0).getSuffixEgJRorIII().setValue(patientName.getSuffix());
        pid.getPatientName(0).getPrefixEgDR().setValue(patientName.getPrefix());
        pid.getPatientName(0).getXpn7_NameTypeCode().setValue(patientName.getType());


        // mother name
        PatientName motherName = patient.getMotherName();
        if (motherName != null) {
            pid.getMotherSMaidenName(0).getFamilyLastName().parse(motherName.getLastName());
            pid.getMotherSMaidenName(0).getGivenName().setValue(motherName.getFirstName());
            pid.getMotherSMaidenName(0).getNameTypeCode().setValue(motherName.getType());
            pid.getPatientName(0).getSuffixEgJRorIII().setValue(patientName.getSuffix());
            pid.getPatientName(0).getPrefixEgDR().setValue(patientName.getPrefix());
        }

        if (patient.getBirthDay() != null) {
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");//设置日期格式
            String birthDay = df.format(patient.getBirthDay());
            pid.getDateTimeOfBirth().parse(birthDay);
        }

        if (patient.getSex() != null) {
            pid.getSex().setValue(patient.getSex().getValue());
        }

        if (patient.getAddress() != null) {
            Address address = patient.getAddress();
            pid.getPatientAddress(0).getStreetAddress().parse(address.getStreetAddressLine1());
            pid.getPatientAddress(0).getOtherDesignation().parse(address.getStreetAddressLine2());
            pid.getPatientAddress(0).getCity().setValue(address.getCity());
            pid.getPatientAddress(0).getStateOrProvince().setValue(address.getState());
            pid.getPatientAddress(0).getZipOrPostalCode().setValue(address.getPostalCode());
            pid.getPatientAddress(0).getCountry().setValue(address.getCountry());
        }

        if (patient.getRace() != null) {
            pid.getRace(0).parse(patient.getRace());
        }

        if (patient.getCounty() != null) {
            pid.getCountyCode().parse(patient.getCounty());
        }

        if (patient.getHomePhoneNumber() != null) {
            pid.getPhoneNumberHome(0).get9999999X99999CAnyText().setValue(patient.getHomePhoneNumber().getFullNumber());
            pid.getPhoneNumberHome(0).getXtn5_CountryCode().setValue(patient.getHomePhoneNumber().getCountryCode());
            pid.getPhoneNumberHome(0).getXtn6_AreaCityCode().setValue(patient.getHomePhoneNumber().getAreaCode());
            pid.getPhoneNumberHome(0).getXtn7_PhoneNumber().setValue(patient.getHomePhoneNumber().getNumber());
            pid.getPhoneNumberHome(0).getXtn8_Extension().setValue(patient.getHomePhoneNumber().getExtension());
            pid.getPhoneNumberHome(0).getXtn9_AnyText().setValue(patient.getHomePhoneNumber().getNote());

        }

        if (patient.getBusinessPhoneNumber() != null) {
            pid.getPhoneNumberBusiness(0).get9999999X99999CAnyText().setValue(patient.getBusinessPhoneNumber().getFullNumber());
            pid.getPhoneNumberBusiness(0).getXtn5_CountryCode().setValue(patient.getBusinessPhoneNumber().getCountryCode());
            pid.getPhoneNumberBusiness(0).getXtn6_AreaCityCode().setValue(patient.getBusinessPhoneNumber().getAreaCode());
            pid.getPhoneNumberBusiness(0).getXtn7_PhoneNumber().setValue(patient.getBusinessPhoneNumber().getNumber());
            pid.getPhoneNumberBusiness(0).getXtn8_Extension().setValue(patient.getBusinessPhoneNumber().getExtension());
            pid.getPhoneNumberBusiness(0).getXtn9_AnyText().setValue(patient.getBusinessPhoneNumber().getNote());
        }

        if (patient.getSsn() != null) {
            pid.getSSNNumberPatient().setValue(patient.getSsn());
        }

        if (patient.getAccountNumber() != null) {
            internalIdentifier = patient.getAccountNumber();
            assigningAuthority = internalIdentifier.getAssigningAuthority();

            // internal patient id
            pid.getPatientAccountNumber().getID().setValue(internalIdentifier.getId());
            pid.getPatientAccountNumber().getCheckDigit().setValue(internalIdentifier.getCheckDigit());
            pid.getPatientAccountNumber().getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(internalIdentifier.getSchemeEmployed());

            pid.getPatientAccountNumber().getAssigningAuthority().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
            pid.getPatientAccountNumber().getAssigningAuthority().getUniversalID().setValue(assigningAuthority.getUniversalId());
            pid.getPatientAccountNumber().getAssigningAuthority().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
            pid.getPatientAccountNumber().getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

            assigningAuthority = internalIdentifier.getAssigningFacility();
            if (assigningAuthority != null) {
                pid.getPatientAccountNumber().getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
                pid.getPatientAccountNumber().getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
                pid.getPatientAccountNumber().getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
            }
        }

        if (patient.getDriverLicense() != null) {
            pid.getDriverSLicenseNumberPatient().getDln1_DriverSLicenseNumber().setValue(patient.getDriverLicense().getLicenseNumber());
            pid.getDriverSLicenseNumberPatient().getDln2_IssuingStateProvinceCountry().setValue(patient.getDriverLicense().getIssuingState());

            SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd");
            if (patient.getDriverLicense().getExpirationDate() != null) {
                String expirationDate = df.format(patient.getDriverLicense().getExpirationDate());
                pid.getDriverSLicenseNumberPatient().getDln3_ExpirationDate().setValue(expirationDate);
            }

        }
    }

    private void fillMRG(MRG mgr, Patient patient) throws HL7Exception {
        PatientIdentifier internalIdentifier = patient.getInternalIdentifier();
        AssigningAuthority assigningAuthority = internalIdentifier.getAssigningAuthority();

        // internal patient id
        mgr.getMrg1_PriorPatientIdentifierList(0).getID().setValue(internalIdentifier.getId());
        mgr.getMrg1_PriorPatientIdentifierList(0).getCheckDigit().setValue(internalIdentifier.getCheckDigit());
        mgr.getMrg1_PriorPatientIdentifierList(0).getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(internalIdentifier.getSchemeEmployed());

        mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningAuthority().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
        mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningAuthority().getUniversalID().setValue(assigningAuthority.getUniversalId());
        mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningAuthority().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());

        mgr.getMrg1_PriorPatientIdentifierList(0).getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

        assigningAuthority = internalIdentifier.getAssigningFacility();
        if(assigningAuthority != null)
        {
            mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
            mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
            mgr.getMrg1_PriorPatientIdentifierList(0).getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
        }

        // alias patient id
        PatientIdentifier aliasIdentifier = patient.getAliasIdentifier();
        if (aliasIdentifier != null) {
            mgr.getMrg2_PriorAlternatePatientID(0).getID().setValue(aliasIdentifier.getId());
            mgr.getMrg2_PriorAlternatePatientID(0).getCheckDigit().setValue(aliasIdentifier.getCheckDigit());
            mgr.getMrg2_PriorAlternatePatientID(0).getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(aliasIdentifier.getSchemeEmployed());

            if (aliasIdentifier.getAssigningAuthority() != null) {
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningAuthority().getNamespaceID().setValue(aliasIdentifier.getAssigningAuthority().getNamespaceId());
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningAuthority().getUniversalID().setValue(aliasIdentifier.getAssigningAuthority().getUniversalId());
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningAuthority().getUniversalIDType().setValue(aliasIdentifier.getAssigningAuthority().getUniversalIDType());
            }

            mgr.getMrg2_PriorAlternatePatientID(0).getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

            assigningAuthority = internalIdentifier.getAssigningFacility();
            if (assigningAuthority != null) {
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
                mgr.getMrg2_PriorAlternatePatientID(0).getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
            }
        }
        PatientIdentifier accountNumber = patient.getAccountNumber();
        if (accountNumber != null) {
            mgr.getMrg3_PriorPatientAccountNumber().getID().setValue(accountNumber.getId());
            mgr.getMrg3_PriorPatientAccountNumber().getCheckDigit().setValue(accountNumber.getCheckDigit());
            mgr.getMrg3_PriorPatientAccountNumber().getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(accountNumber.getSchemeEmployed());

            if (accountNumber.getAssigningAuthority() != null) {
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningAuthority().getNamespaceID().setValue(accountNumber.getAssigningAuthority().getNamespaceId());
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningAuthority().getUniversalID().setValue(accountNumber.getAssigningAuthority().getUniversalId());
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningAuthority().getUniversalIDType().setValue(accountNumber.getAssigningAuthority().getUniversalIDType());
            }
            mgr.getMrg3_PriorPatientAccountNumber().getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

            assigningAuthority = internalIdentifier.getAssigningFacility();
            if (assigningAuthority != null) {
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
                mgr.getMrg3_PriorPatientAccountNumber().getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
            }
        }

        if (patient.getExternalIdentifier() != null) {
            mgr.getMrg4_PriorPatientID().getID().setValue(internalIdentifier.getId());
            mgr.getMrg4_PriorPatientID().getCheckDigit().setValue(internalIdentifier.getCheckDigit());
            mgr.getMrg4_PriorPatientID().getCodeIdentifyingTheCheckDigitSchemeEmployed().setValue(internalIdentifier.getSchemeEmployed());

            mgr.getMrg4_PriorPatientID().getAssigningAuthority().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
            mgr.getMrg4_PriorPatientID().getAssigningAuthority().getUniversalID().setValue(assigningAuthority.getUniversalId());
            mgr.getMrg4_PriorPatientID().getAssigningAuthority().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
        }

        mgr.getMrg4_PriorPatientID().getIdentifierTypeCode().setValue(internalIdentifier.getIdentifierTypeCode());

        assigningAuthority = internalIdentifier.getAssigningFacility();
        if (assigningAuthority != null) {
            mgr.getMrg4_PriorPatientID().getAssigningFacility().getNamespaceID().setValue(assigningAuthority.getNamespaceId());
            mgr.getMrg4_PriorPatientID().getAssigningFacility().getUniversalID().setValue(assigningAuthority.getUniversalId());
            mgr.getMrg4_PriorPatientID().getAssigningFacility().getUniversalIDType().setValue(assigningAuthority.getUniversalIDType());
        }

        // patient name
        PatientName patientName = patient.getPatientName();
        mgr.getMrg7_PriorPatientName(0).getFamilyLastName().parse(patientName.getLastName());
        mgr.getMrg7_PriorPatientName(0).getGivenName().parse(patientName.getFirstName());
        mgr.getMrg7_PriorPatientName(0).getNameTypeCode().setValue(patientName.getType());
        mgr.getMrg7_PriorPatientName(0).getSuffixEgJRorIII().setValue(patientName.getSuffix());
        mgr.getMrg7_PriorPatientName(0).getPrefixEgDR().setValue(patientName.getPrefix());
    }

    // http://hl7api.sourceforge.net/getting_started.html
    public String registerInPatient(Patient patient) throws IOException, HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        ADT_A01 adt = new ADT_A01();
        adt.initQuickstart("ADT", "A01", "P");

        // Populate the MSH Segment
        MSH msh = adt.getMSH();
        this.fillMSH(msh, "ADT_A01");

        // Event Date
        EVN evn = adt.getEVN();
        evn.getRecordedDateTime().parse(now);

        // Populate the PID Segment
        PID pid = adt.getPID();
        this.fillPID(pid, patient);

        PV1 pv1 = adt.getPV1();
        pv1.getPatientClass().setValue("I");

        // how to create a patient
        String sMessage = adt.toString().replace("\r", "\r\n");
        String result = sendMessage(sMessage);

        logger.info("Request: " + sMessage);
        logger.info("Response: " +result);

        return result;
    }

    public String registerOutPatient(Patient patient) throws IOException, HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        ADT_A04 adt = new ADT_A04();
        adt.initQuickstart("ADT", "A04", "P");

        // Populate the MSH Segment
        MSH msh = adt.getMSH();
        this.fillMSH(msh, "ADT_A01");

        // Event Date
        EVN evn = adt.getEVN();
        evn.getRecordedDateTime().parse(now);

        // Populate the PID Segment
        PID pid = adt.getPID();
        this.fillPID(pid, patient);

        PV1 pv1 = adt.getPV1();
        pv1.getPatientClass().setValue("O");

        // how to create a patient
        String sMessage = adt.toString().replace("\r", "\r\n");
        String result = sendMessage(sMessage);
        return result;
    }

    public String preregisterPatient(Patient patient) throws IOException, HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        ADT_A05 adt = new ADT_A05();
        adt.initQuickstart("ADT", "A05", "P");

        // Populate the MSH Segment
        MSH msh = adt.getMSH();
        this.fillMSH(msh, "ADT_A01");

        // Event Date
        EVN evn = adt.getEVN();
        evn.getRecordedDateTime().parse(now);

        // Populate the PID Segment
        PID pid = adt.getPID();
        this.fillPID(pid, patient);

        PV1 pv1 = adt.getPV1();
        pv1.getPatientClass().setValue("P");

        // how to create a patient
        String sMessage = adt.toString().replace("\r", "\r\n");
        String result = sendMessage(sMessage);
        return result;
    }

    public String updatePatient(Patient patient) throws IOException, HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        ADT_A05 adt = new ADT_A05();
        adt.initQuickstart("ADT", "A08", "P");

        // Populate the MSH Segment
        MSH msh = adt.getMSH();
        this.fillMSH(msh, "ADT_A01");

        // Event Date
        EVN evn = adt.getEVN();
        evn.getRecordedDateTime().parse(now);

        // Populate the PID Segment
        PID pid = adt.getPID();
        this.fillPID(pid, patient);

        PV1 pv1 = adt.getPV1();
        pv1.getPatientClass().setValue("R");

        // how to create a patient
        String sMessage = adt.toString().replace("\r", "\r\n");
        String result = sendMessage(sMessage);

        logger.info("Request: " + sMessage);
        logger.info("Response: " +result);

        return result;
    }

    public String mergePatient(Patient patient, Patient mgrPatient) throws IOException, HL7Exception {
        SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");//设置日期格式
        String now = df.format(new Date());

        ADT_A40 adt = new ADT_A40();
        adt.initQuickstart("ADT", "A40", "P");

        // Populate the MSH Segment
        MSH msh = adt.getMSH();
        // msh.getMsh12_VersionID().getVersionID().setValue("2.5");
        this.fillMSH(msh, "ADT_A39");

        // Event Date
        EVN evn = adt.getEVN();
        evn.getRecordedDateTime().parse(now);

        // Populate the PID Segment
        PID pid = adt.getPIDPD1MRGPV1().getPID();
        this.fillPID(pid, patient);

        // Populate the PID Segment
        MRG mgr = adt.getPIDPD1MRGPV1().getMRG();
        this.fillMRG(mgr, mgrPatient);

        // Populate the PID Segment

        // how to create a patient
        String sMessage = adt.toString().replace("\r", "\r\n");
        String result = sendMessage(sMessage);

        logger.info("Request: " + sMessage);
        logger.info("Response: " +result);
        return result;
    }


    @Override
    protected String getHost() {
        return host;
    }

    @Override
    protected int getPort() {
        return port;
    }
}
